package com.dayatang.hrm.compensation.domain;

import java.util.Date;

import javax.persistence.Column;
import javax.persistence.DiscriminatorColumn;
import javax.persistence.DiscriminatorType;
import javax.persistence.Embedded;
import javax.persistence.Entity;
import javax.persistence.Inheritance;
import javax.persistence.InheritanceType;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.Table;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.context.support.MessageSourceAccessor;
import org.springframework.util.Assert;

import com.dayatang.commons.domain.AbstractEntity;
import com.dayatang.commons.domain.DataType;
import com.dayatang.commons.domain.InstanceFactory;
import com.dayatang.hrm.compensation.context.CompensationMessageSource;
import com.dayatang.hrm.compensation.domain.exception.CompensationRuntimeException;
import com.dayatang.hrm.compensation.policy.DataCalculatePolicy;
import com.dayatang.hrm.compensation.utils.DateVersionUtils;
import com.dayatang.hrm.compensation.vo.FiscalPeriodRangeSetting;
import com.dayatang.utils.DateUtils;

@Entity
@Table(name = "pay_sp_setting")
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(name = "sp_setting_category", discriminatorType = DiscriminatorType.INTEGER)
public abstract class SalaryParameterSetting extends AbstractEntity {

	/**
	 * 
	 */
	private static final long serialVersionUID = -3040379611253949340L;

	private static final Logger logger = LoggerFactory
			.getLogger(SalaryParameterSetting.class);

	private static SalaryParameterSettingRepository salaryParameterSettingRepository;

	protected static MessageSourceAccessor messages = CompensationMessageSource
			.getAccessor();

	// 参数名称
	@Column(name = "name")
	private String name;

	@Column(name = "is_need_tag_version")
	private Boolean needTagVersion;

	// 数据类型
	@Column(name = "data_type")
	private DataType dataType;
	// 排序号
	@Column(name = "order_seq")
	private Integer orderSeq;
	// 数据计算策略（直接取值、求和、求平均、求最大值、求最小值）
	@ManyToOne
	@JoinColumn(name = "data_calculate_policy_id")
	private DataCalculatePolicy dataCalculatePolicy;
	// 取值范围设置
	@Embedded
	private FiscalPeriodRangeSetting fiscalPeriodRangeSetting;
	// 备注
	@Column(name = "remark", length = 200)
	private String remark;

	@Column(name = "from_date", nullable = false)
	@Temporal(TemporalType.TIMESTAMP)
	private Date fromDate;

	@Column(name = "to_date")
	@Temporal(TemporalType.TIMESTAMP)
	private Date toDate = DateUtils.MAX_DATE;

	// 创建时间
	@Column(name = "created_time")
	@Temporal(TemporalType.TIMESTAMP)
	private Date created;

	@ManyToOne
	@JoinColumn(name = "salary_parameter_id")
	private SalaryParameter salaryParameter;

	public SalaryParameterSetting() {
	}

	public SalaryParameterSetting(SalaryParameter salaryParameter) {
		this.salaryParameter = salaryParameter;
	}

	public static SalaryParameterSettingRepository getSalaryParameterSettingRepository() {
		if (salaryParameterSettingRepository == null) {
			salaryParameterSettingRepository = InstanceFactory
					.getInstance(SalaryParameterSettingRepository.class);
		}
		return salaryParameterSettingRepository;
	}

	public static void setSalaryParameterSettingRepository(
			SalaryParameterSettingRepository salaryParameterSettingRepository) {
		SalaryParameterSetting.salaryParameterSettingRepository = salaryParameterSettingRepository;
	}

	public static MessageSourceAccessor getMessages() {
		return messages;
	}

	public static void setMessages(MessageSourceAccessor messages) {
		SalaryParameterSetting.messages = messages;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public Boolean isNeedTagVersion() {
		return needTagVersion;
	}

	public Boolean getNeedTagVersion() {
		return needTagVersion;
	}

	public void setNeedTagVersion(Boolean needTagVersion) {
		this.needTagVersion = needTagVersion;
	}

	public DataType getDataType() {
		return dataType;
	}

	public void setDataType(DataType dataType) {
		this.dataType = dataType;
	}

	public Integer getOrderSeq() {
		return orderSeq;
	}

	public void setOrderSeq(Integer orderSeq) {
		this.orderSeq = orderSeq;
	}

	public DataCalculatePolicy getDataCalculatePolicy() {
		return dataCalculatePolicy;
	}

	public void setDataCalculatePolicy(DataCalculatePolicy dataCalculatePolicy) {
		this.dataCalculatePolicy = dataCalculatePolicy;
	}

	public FiscalPeriodRangeSetting getFiscalPeriodRangeSetting() {
		return fiscalPeriodRangeSetting;
	}

	public void setFiscalPeriodRangeSetting(
			FiscalPeriodRangeSetting fiscalPeriodRangeSetting) {
		this.fiscalPeriodRangeSetting = fiscalPeriodRangeSetting;
	}

	public String getRemark() {
		return remark;
	}

	public void setRemark(String remark) {
		this.remark = remark;
	}

	public Date getCreated() {
		return created;
	}

	public void setCreated(Date created) {
		this.created = created;
	}

	public Date getFromDate() {
		return fromDate;
	}

	public void setFromDate(Date fromDate) {
		this.fromDate = fromDate;
	}

	public Date getToDate() {
		return toDate;
	}

	public void setToDate(Date toDate) {
		this.toDate = toDate;
	}

	public SalaryParameter getSalaryParameter() {
		return salaryParameter;
	}

	public void setSalaryParameter(SalaryParameter salaryParameter) {
		this.salaryParameter = salaryParameter;
	}

	@Override
	public boolean equals(Object arg0) {
		// TODO Auto-generated method stub
		return false;
	}

	@Override
	public int hashCode() {
		// TODO Auto-generated method stub
		return 0;
	}

	@Override
	public String toString() {
		// TODO Auto-generated method stub
		return null;
	}

	/*
	 * =======================================
	 * 
	 * action
	 * 
	 * ========================================
	 */

	private void save() {
		if (isNew()) {
			setCreated(new Date());

			if (fromDate == null) {
				setFromDate(created);
			}

			if (logger.isDebugEnabled()) {
				logger.debug("准备新增设置：name【{}】，fromDate【{}】，toDate【{}】",
						new Object[] { name, fromDate, toDate });
			}

			validateName(name, fromDate, toDate);
			getSalaryParameterSettingRepository().save(this);
		} else {
			if (isNeedTagVersion()) {
				Date markedDate = new Date();

				// 取出原有设置
				SalaryParameterSetting setting = getPure(this);
				// 设置结束日期（MySQL不能精确到毫秒）
				setting.setToDate(DateVersionUtils
						.getPreviousVersionTimestamp(markedDate));
				// 不需要验证名称
				// validateName(name, fromDate, toDate);
				if (logger.isDebugEnabled()) {
					logger
							.debug("准备备份原有设置：name【{}】，fromDate【{}】，toDate【{}】",
									new Object[] { setting.getName(),
											setting.getFromDate(),
											setting.getToDate() });
				}
				getSalaryParameterSettingRepository().save(setting);

				// 作为新对象保存
				SalaryParameterSetting newInstance = getNewInstance(salaryParameter);
				BeanUtils.copyProperties(this, newInstance);
				newInstance.setId(null);
				newInstance.setFromDate(null);
				newInstance.setVersion(0);
				newInstance.setCreated(DateVersionUtils
						.getCurrentlyVersionTimestamp(markedDate));
				newInstance.save();
			} else {

				if (logger.isDebugEnabled()) {
					logger.debug(
							"准备更正设置（不备份）：name【{}】，fromDate【{}】，toDate【{}】",
							new Object[] { name, fromDate, toDate });
				}

				validateName(name, fromDate, toDate);
				getSalaryParameterSettingRepository().save(this);
			}
		}
	}

	/**
	 * 改正设置并保存（不会将设置备份）
	 */
	public void saveChanges() {
		setNeedTagVersion(false);
		save();
	}

	/**
	 * 备份原有设置并保存当前设置（会将之前的设置备份）
	 */
	public void archive() {
		setNeedTagVersion(true);
		save();
	}

	public void abolish(Date abolishDate) {
		if (abolishDate == null) {
			abolishDate = new Date();
		}
		setToDate(abolishDate);
		save();
	}

	/**
	 * 验证薪资参数名称有效性
	 * 
	 * @param name
	 * @param fromDate
	 * @param toDate
	 */
	public void validateName(String name, Date fromDate, Date toDate) {
		Assert.notNull(name, messages.getMessage(
				"SalaryParameterSetting.validateName.nameIsNull",
				"薪资参数的名称不允许为空。"));

		if (isNameAlreadyUsedByOtherInSameSalaryAccountSuite(name, fromDate,
				toDate)) {
			throw new CompensationRuntimeException(messages.getMessage(
					"SalaryParameterSetting.validateName.nameAlreadyUsed",
					"该薪资参数的名称已经被使用。"));
		}
	}

	public abstract SalaryParameterSetting getNewInstance(
			SalaryParameter salaryParameter);

	public abstract boolean isNameAlreadyUsedByOtherInSameSalaryAccountSuite(
			String name, Date fromDate, Date toDate);

	public static SalaryParameterSetting get(Long id) {
		return getSalaryParameterSettingRepository().get(id);
	}

	private SalaryParameterSetting getPure(
			SalaryParameterSetting salaryParameterSetting) {
		return getSalaryParameterSettingRepository().getPure(
				salaryParameterSetting);
	}
}
